/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.lang;

/**
 * 
 * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
 *
 */
public final class Maths {
    
    private Maths() {}
    
    /**
     * 如果两根线是连接在一起的则返回true
     * @param line1
     * @param line2
     * @return
     */
    public static boolean isLink(Line line1, Line line2) {
        Checks.verifyNotNull(line1, "line1");
        Checks.verifyNotNull(line2, "line2");
        if (line1.equals(line2)) {
            return false;
        }
        return line1.begin.equals(line2.begin) || line1.begin.equals(line2.end)
                || line1.end.equals(line2.begin) || line1.end.equals(line2.end);
    }
    
    /**
     * 如果指定的线能连成一个区域则返回true
     * @param lines
     * @return
     */
    public static boolean isRegion(Line... lines) {
        Checks.verifyNotNull(lines, "lines");
        if (lines.length < 3) {
            return false;
        }
        for (int i = 1; i < lines.length; i++) {
            Checks.verifyNotNull(lines[i], "lines[" + i + "]");
            if (!isLink(lines[i - 1], lines[i])) {
                return false;
            }
        }
        return isLink(lines[lines.length - 1], lines[0]);
    }
    
    /**
     * 获取以点0为顶点，引出两条线到边点1、边点2所产生的角度。
     *
     * 此方法可用做判断某点是否在多个点连线形成的区域内，方法是计算该点到区域各顶点的角度之和，
     * 如果是360度则在区域内，否则在区域外。
     *
     * @param dot 顶点
     * @param dot1 边点1
     * @param dot2 边点2
     * @return 返回三点形成的角度
     */
    public static double getAngle(Dot dot, Dot dot1, Dot dot2) {
        double ma_x = dot1.x - dot.x;
        double ma_y = dot1.y - dot.y;
        double mb_x = dot2.x - dot.x;
        double mb_y = dot2.y - dot.y;
        double v1 = (ma_x * mb_x) + (ma_y * mb_y);
        double ma_val = Math.sqrt(ma_x * ma_x + ma_y * ma_y);
        double mb_val = Math.sqrt(mb_x * mb_x + mb_y * mb_y);
        double cosM = v1 / (ma_val * mb_val);
        return Math.acos(cosM) * 180 / Math.PI;
    }
    
    /**
     * 点
     * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
     *
     */
    public static class Dot {
        
        public final int x;
        
        public final int y;
        
        public Dot(int x, int y) {
            this.x = x;
            this.y = y;
        }
        
        public boolean equals(int x, int y) {
            return this.x == x && this.y == y;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + x;
            result = prime * result + y;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Dot other = (Dot) obj;
            if (x != other.x)
                return false;
            if (y != other.y)
                return false;
            return true;
        }
        
    }
    
    /**
     * 线
     * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
     *
     */
    public static class Line {
        
        public final Dot begin;
        
        public final Dot end;
        
        public Line(Dot begin, Dot end) {
            Checks.verifyNotNull(begin, "begin");
            Checks.verifyNotNull(end, "end");
            this.begin = begin;
            this.end = end;
        }
        
        public boolean equals(Dot begin, Dot end) {
            return this.begin.equals(begin) && this.end.equals(end)
                    || this.begin.equals(end) && this.end.equals(begin);
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + begin.hashCode() + end.hashCode();
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            Line other = (Line) obj;
            return this.equals(other.begin, other.end);
        }
        
    }

}
